#pragma once

#include "designpattern/instantiator.hpp"
#include "stdlib/bugcheck.hpp"
#include "stdlib/exception.hpp"
using namespace JHCPP::stdlib;
using namespace JHCPP::stdlib::jhException;
#include "event/mutex.hpp"
using namespace JHCPP::event;
#include <map>
#include <memory>

namespace JHCPP
{
	namespace designpattern
	{
		template <typename T>
		class TFactory
		{
		public:
			typedef TAbstractInstantiator<T> AbstractFactory;

			TFactory()
			{
			}

			~TFactory()
			{
				for (typename FactoryMap::iterator it = m_map.begin(); it != m_map.end(); ++it)
				{
					delete it->second;
				}
			}

			T* createInstance(const std::string& className) const
			{
				CMutex::ScopedLock lock(m_mutex);

				typename FactoryMap::const_iterator it = m_map.find(className);
				if (it != m_map.end())
					return it->second->createInstance();
				else
					throw NotFoundException(className);
			}

			template <class C> 
			void registerClass(const std::string& className)
			{
				registerClass(className, new TInstantiator<C, T>);
			}

			void registerClass(const std::string& className, AbstractFactory* pAbstractFactory)
			{
				jh_check_ptr (pAbstractFactory);

				CMutex::ScopedLock lock(m_mutex);

				std::auto_ptr<AbstractFactory> ptr(pAbstractFactory);
				typename FactoryMap::iterator it = m_map.find(className);
				if (it == m_map.end())
					m_map[className] = ptr.release();
				else
					throw ExistsException(className);
			}

			void unregisterClass(const std::string& className)
			{
				CMutex::ScopedLock lock(m_mutex);

				typename FactoryMap::iterator it = m_map.find(className);
				if (it != m_map.end())
				{
					delete it->second;
					m_map.erase(it);
				}
				else throw NotFoundException(className);
			}

			bool isClass(const std::string& className) const
			{
				CMutex::ScopedLock lock(m_mutex);

				return m_map.find(className) != m_map.end();
			}

		private:
			TFactory(const TFactory&);
			TFactory& operator = (const TFactory&);

			typedef std::map<std::string, AbstractFactory*> FactoryMap;

			FactoryMap		m_map;
			mutable CMutex	m_mutex;
		};
	}//end of namespace DESIGNPATTERN
}//end of namespace JHCPP
